﻿using System.Diagnostics;
#if NETFX_CORE
using Windows.UI.Xaml;
#else
using System.Windows;
#if !SILVERLIGHT
using System.Windows.Input;
#endif
#endif

namespace FreshMeat.Xaml
{
   /// <summary>
   /// An attached behavior which causes an element to attempt to move the focus to a child whenever 
   /// it receives the focus itself.
   /// </summary>
   /// <remarks>
   /// In certain situations, WPF/Silverlight moves the focus to a parent focus scope (typically,
   /// a Window) but you don't want that element to have the focus (for example, a Window has nothing
   /// to indicate that it has the focus).  This can happen when the child that has the focus becomes
   /// disabled or invisible, in which case WPF/Silverlight goes up through the hierarchy until it 
   /// finds something that can have the focus (the Window, in this example).  Now, there most likely
   /// is a child that can receive the focus, but it wasn't a parent of the child that previously
   /// had the focus; instead, it's typically in some other subtree of the Window.  Long story short,
   /// do this in the parent element that receives the focus but doesn't want it:
   ///    RefocusBehavior.IsEnabled="True"
   /// Now, when that element gets the focus, it will immediately try to move the focus to the next
   /// focusable element beneath it.
   /// </remarks>
   public class RefocusBehavior : AttachedBehavior<RefocusBehavior, FrameworkElement>
   {
      static readonly BooleanSwitch trace = new BooleanSwitch("Focus", "Focus");

      /// <summary>
      /// Determines whether behavior is enabled.
      /// </summary>
      public readonly static DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached(
         "IsEnabled",
         typeof(bool),
         typeof(RefocusBehavior),
         new FrameworkPropertyMetadata(false, (d, e) => GetInstance(d)));

      public static bool GetIsEnabled(DependencyObject d)
      {
         return (bool)d.GetValue(IsEnabledProperty);
      }

      public static void SetIsEnabled(DependencyObject d, bool value)
      {
         d.SetValue(IsEnabledProperty, value);
      }

      protected override void OnAttached()
      {
         var associatedObject = TryGetAssociatedObject();
#if !SILVERLIGHT && !NETFX_CORE
         associatedObject.GotKeyboardFocus += OnAssociatedObjectGotKeyboardFocus;
#else
         associatedObject.GotFocus += OnAssociatedObjectGotFocus;
#endif
      }

      protected override void OnDetaching()
      {
         var associatedObject = TryGetAssociatedObject();
         if (associatedObject == null)
            return;

#if !SILVERLIGHT && !NETFX_CORE
         associatedObject.GotKeyboardFocus -= OnAssociatedObjectGotKeyboardFocus;
#else
         associatedObject.GotFocus += OnAssociatedObjectGotFocus;
#endif
      }

#if !SILVERLIGHT && !NETFX_CORE
      void OnAssociatedObjectGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
#else
      void OnAssociatedObjectGotFocus(object sender, RoutedEventArgs e)
#endif
      {
         var associatedObject = TryGetAssociatedObject();
         if (associatedObject == null)
            return;

         if (!GetIsEnabled(associatedObject))
            return;

#if !SILVERLIGHT && !NETFX_CORE
         var gotFocus = e.NewFocus;
#else
         var gotFocus = e.OriginalSource;
#endif
         if (!ReferenceEquals(gotFocus, associatedObject))
            return;

         if (trace.Enabled)
            Trace.TraceInformation("RefocusHehavior is moving focus to next item.");
         FocusHelper.MoveFocusNext(associatedObject);
      }
   }
}
